﻿
using System;
using System.ComponentModel;
using System.Threading.Tasks;

namespace Red.Core
{
    /// <summary>
    ///     Non-blocking task value wrapper. For the time until the task finishes, its Result is just default(TResult).
    ///     May be used as a type for binding-friendly async properties.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public sealed class BindableAsyncValue<T> : ViewModel
    {
        #region Properties

        public Task<T> Task { get; private set; }

        public T Result
        {
            get { return ( Task.Status == TaskStatus.RanToCompletion ) ? Task.Result : default( T ); }
        }

        public TaskStatus Status
        {
            get { return Task.Status; }
        }

        public bool IsCompleted
        {
            get { return Task.IsCompleted; }
        }

        public bool IsSuccessfullyCompleted
        {
            get { return Task.Status == TaskStatus.RanToCompletion; }
        }

        public bool IsCanceled
        {
            get { return Task.IsCanceled; }
        }

        public bool IsFaulted
        {
            get { return Task.IsFaulted; }
        }

        public AggregateException Exception
        {
            get { return Task.Exception; }
        }

        public Exception InnerException
        {
            get { return ( Exception == null ) ? null : Exception.InnerException; }
        }

        public string ErrorMessage
        {
            get { return ( InnerException == null ) ? null : InnerException.Message; }
        }

        #endregion Properties

        #region C-tor
        
        public BindableAsyncValue( Task<T> task )
        {
            Task = task;
            if ( !task.IsCompleted )
            {
                var _ = WatchTaskAsync( task );
            }
        }
        
        #endregion C-tor

        #region Private methods
        
        private async Task WatchTaskAsync( Task task )
        {
            try
            {
                await task;
            }
            catch
            {
            }

            OnPropertyChanged( "Status" );
            OnPropertyChanged( "IsCompleted" );

            if ( task.IsCanceled )
            {
                OnPropertyChanged( "IsCanceled" );
            }
            else if ( task.IsFaulted )
            {
                OnPropertyChanged( "IsFaulted" );
                OnPropertyChanged( "Exception" );
                OnPropertyChanged( "InnerException" );
                OnPropertyChanged( "ErrorMessage" );
            }
            else
            {
                OnPropertyChanged( "IsSuccessfullyCompleted" );
                OnPropertyChanged( "Result" );
            }
        }
        
        #endregion Private methods

    }
}